package com.terrapages.wmsc4j.layer.tile;

import java.awt.Dimension;
import java.awt.image.BufferedImage;

import com.terrapages.wmsc4j.bean.BBox;
import com.terrapages.wmsc4j.bean.Pos;
import com.terrapages.wmsc4j.layer.AbstractLayer;
import com.terrapages.wmsc4j.layer.resolution.Resolution;

/**
 * A tile represents all the images within a cache in a grid structure. 
 * Each instance is the 'xIndex+1'th tile across
 * and the 'yIndex+1'th tile up, from the layer origin, at the 'zoom+1'th zoom level. The reason for the +1 
 * is that everything starts at zero. ie If we are looking at tile x=0 y=0, we are looking at the 
 * 1st tile across and the 1st tile up.
 * 
 * Depending on where the origin is, it is possible for x and y to be negative,
 * but zoom is always positive.
 * 
 * @author mvivian
 * @date 17 May 2007
 */
public class Tile {
	final private transient AbstractLayer layer;
	private int zoom;
	private int xIndex;
	private int yIndex;
	private BufferedImage image = null;
	
	/**
	 * Creates a tile, with information excluding the image, from the given layer at the given x,y index and zoom.
	 * 
	 * @param layer The layer the tile belongs to
	 * @param zoom The zoom level of the tile
	 * @param xIndex The 'xIndex+1'th tile across
	 * @param yIndex The 'yIndex+1'th tile up
	 */
	public Tile(final AbstractLayer layer, final int zoom, final int xIndex, final int yIndex) {
		super();
		this.layer = layer;
		this.zoom = zoom;
		this.xIndex = xIndex;
		this.yIndex = yIndex;
	}
	
	/**
	 * Creates a tile, with information excluding the image, 
	 * from the given layer that that contains the given coordinate,
	 * at the closest zoom level to the given Resolution.
	 * 
	 * @param layer Tile belongs to this Layer
	 * @param coordinate Tile contains this coordinate
	 * @param resolution Tile is at approximatly this resolution
	 */
	public Tile(final AbstractLayer layer, final Pos coordinate, final Resolution resolution)
	{
		final Pos origin = layer.getCoverage().getMinPos();

		this.layer = layer;
		
		xIndex=(int)((coordinate.getLongitude() - origin.getLongitude()) / ( resolution.getValue() * getTileSize().getWidth()));
		yIndex=(int)((coordinate.getLatitude() - origin.getLatitude()) / ( resolution.getValue() * getTileSize().getHeight()));
		
		zoom = layer.getZoomLevel(resolution);
	}
	
	/**
	 * Gets the size of this tile in pixels
	 * @return Dimesion with height/width attributes
	 */
	public Dimension getTileSize() {
		return layer.getFormat().getTileSize();
	}

	/**
	 * Get the x index of the tile
	 * @return int
	 */
	public final int getXIndex() {
		return xIndex;
	}
	
	/**
	 * Set the x index of the tile
	 */
	public final void setXIndex(final int xIndex) {
		this.xIndex = xIndex;
	}
	
	/**
	 * Get the y index of the tile
	 * @return int
	 */
	public final int getYIndex() {
		return yIndex;
	}

	/**
	 * Set the y index of the tile
	 */
	public final void setYIndex(final int yIndex) {
		this.yIndex = yIndex;
	}
	
	/**
	 * Get the zoom of the tile
	 * @return int
	 */
	public final int getZoom() {
		return zoom;
	}
	
	/**
	 * Set the zoom of the tile
	 * @return int
	 */
	public final void setZoom(final int zoom) {
		this.zoom = zoom;
	}
	
	/**
	 * Based on the layer the tile belongs to and the position of the tile on the layer and
	 * the zoom level of the tile, this method calculates and gives the Bounding Box 
	 * this tile represents. 
	 * @return int
	 */
	public final BBox getBoundingBox()
	{
		final Pos origin = layer.getOrigin();
		final Resolution resolution = getResolution();
		final BBox retBoundingBox = new BBox(
				 xIndex      * resolution.getValue() * getTileSize().width  + origin.getLongitude() ,
				 yIndex      * resolution.getValue() * getTileSize().height + origin.getLatitude()  ,
				(xIndex + 1) * resolution.getValue() * getTileSize().width  + origin.getLongitude() ,
				(yIndex + 1) * resolution.getValue() * getTileSize().height + origin.getLatitude()
		);
		return retBoundingBox;
	}
	
	/**
	 * Returns the resolution of this tile based on the zoom.
	 * @return
	 */
	public final Resolution getResolution(){
		return layer.getResolution(zoom);
	}

	/**
	 * Gives a string representation of the tile.<br>
	 * Eg.<br>
	 * "(20, 53, 1)"<br>
	 * Where 20 = x; 53 = y; zoom = 1;
	 */
	public String toString()
	{
		return "(x = " + xIndex + ", y = " + yIndex + ", z = " + zoom + ")";
	}

	/**
	 * Gives the layer this tile belongs to.
	 * @return
	 */
	public AbstractLayer getLayer() {
		return layer;
	}

	/**
	 * Returns the image of the tile. 
	 * Null is returned, if the tile's image has not yet been retreived from the layer's cache.
	 * @return BufferedImage
	 */
	public final BufferedImage getImage() {
		return image;
	}

	/**
	 * Sets the image of the tile. 
	 * Does not cache the image of the tile.
	 * 
	 */
	public final void setImage(final BufferedImage image) {
		this.image = image;
	}
}
